@page "/todomvc"
@inherits AppComponentBase
@rendermode RenderMode.InteractiveServer

<Heading1>Todos Application</Heading1>

<form @onsubmit="addTodo" class="max-w-xl">
<input type="submit" class="hidden">
<CascadingValue Value=@errorStatus>

    <ErrorSummary Except=@VisibleFields />

    <TextInput placeholder="What needs to be done?" @bind-Value="request.Text" Label="" />

    <div class="mt-4 bg-white dark:bg-black shadow overflow-hidden rounded-md">
        <ul role="list" class="divide-y divide-gray-200 dark:divide-gray-800">
        @foreach (var todo in filteredTodos())
        {
            <li @key="todo.Id" class="px-6 py-4">
                <div class="relative flex items-start" @onclick="_ => toggleTodo(todo.Id)" @onclick:stopPropagation>
                    <div class="flex items-center h-6">
                        @if (todo.IsFinished)
                        {
                            <svg class="cursor-pointer text-green-600 dark:text-green-400 h-5 w-5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
                                <path fill="currentColor" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10s10-4.5 10-10S17.5 2 12 2m-2 15l-5-5l1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9Z"></path>
                            </svg>
                        }
                        else
                        {
                            <svg class="cursor-pointer h-5 w-5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
                                <path fill="currentColor" d="M12 20a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path>
                            </svg>
                        }
                    </div>
                    <div class="ml-3 flex-grow">
                        <label class=@(todo.IsFinished ? "line-through" : "")>@todo.Text</label>
                    </div>
                    <div>
                        @if (todo.IsFinished)
                        {
                            <svg class="cursor-pointer h-5 w-5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"
                                 @onclick="_ => removeTodo(todo.Id)" @onclick:stopPropagation>
                                <path fill="currentColor" d="M9 3v1H4v2h1v13a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6h1V4h-5V3H9M7 6h10v13H7V6m2 2v9h2V8H9m4 0v9h2V8h-2Z"></path>
                            </svg>
                        }
                    </div>
                </div>
            </li>
        }
        </ul>
    </div>

    <div class="mt-4 flex justify-between">
        <div class="text-gray-400 dark:text-gray-500 leading-8 mr-4">
            @unfinishedTodos().Count() <span class="hidden sm:inline">item(s)</span> left
        </div>

        <div class="inline-flex shadow rounded-md">
            <button class=@TabClass("rounded-l-lg border dark:text-white", filter == Filter.All) @onclick=@(() => filter = Filter.All) @onclick:preventDefault>
                All
            </button>
            <button class=@TabClass("border-t border-b dark:text-white", filter == Filter.Unfinished) @onclick=@(() => filter = Filter.Unfinished) @onclick:preventDefault>
                Active
            </button>
                <button class=@TabClass("rounded-r-md border dark:text-white", filter == Filter.Finished) @onclick=@(() => filter = Filter.Finished) @onclick:preventDefault>
                Completed
            </button>
        </div>

        <div class="leading-8 ml-4">
            <button class=@(finishedTodos().Count() == 0 ? "invisible" : "") @onclick="_ => removeFinishedTodos()" @onclick:preventDefault>
                clear <span class="hidden sm:inline">completed</span>
            </button>
        </div>
    </div>

</CascadingValue>
</form>

@code {
    enum Filter
    {
        All,
        Finished,
        Unfinished
    }
    static string[] VisibleFields = new[] { nameof(CreateTodo.Text) };

    string TabClass(string @class, bool isActive) =>
        ClassNames("border-gray-200 dark:border-gray-800 text-sm font-medium px-4 py-2 hover:bg-gray-100 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700",
            (isActive ? "text-blue-700 dark:bg-blue-600" : "text-gray-900 hover:text-blue-700 dark:bg-gray-700"), @class);

    List<Todo> todos = new();
    Filter filter = Filter.All;

    CreateTodo request = new();
    ResponseStatus? errorStatus;

    IEnumerable<Todo> filteredTodos() => filter switch
    {
        Filter.Finished => finishedTodos(),
        Filter.Unfinished => unfinishedTodos(),
        _ => todos
    };
    IEnumerable<Todo> finishedTodos() => todos.Where(x => x.IsFinished);
    IEnumerable<Todo> unfinishedTodos() => todos.Where(x => !x.IsFinished);

    protected override async Task OnInitializedAsync() => await refreshTodos();

    // For best UX: apply changes locally then revalidate with real server state
    async Task refreshTodos()
    {
        var api = await ApiAsync(new QueryTodos());
        if (api.Succeeded)
            todos = api.Response!.Results;
        else
            errorStatus = api.Error;
    }

    async Task addTodo()
    {
        errorStatus = null;
        todos.Add(new Todo { Text = request.Text });
        var api = await ApiAsync(request);
        if (api.Succeeded)
            request.Text = "";
        else
            errorStatus = api.Error;
        await refreshTodos();
    }

    async Task removeTodo(long id)
    {
        todos.RemoveAll(x => x.Id == id);
        var api = await ApiAsync(new DeleteTodos { Ids = [id] });
        errorStatus = api.Error;
        await refreshTodos();
    }

    async Task removeFinishedTodos()
    {
        var ids = todos.Where(x => x.IsFinished).Select(x => x.Id).ToList();
        if (ids.Count == 0) return;
        todos.RemoveAll(x => ids.Contains(x.Id));
        var api = await ApiAsync(new DeleteTodos { Ids = ids });
        errorStatus = api.Error;
        await refreshTodos();
    }

    async Task toggleTodo(long id)
    {
        var todo = todos.Find(x => x.Id == id)!;
        todo.IsFinished = !todo.IsFinished;
        var api = await ApiAsync(new UpdateTodo { Id = todo.Id, Text = todo.Text, IsFinished = todo.IsFinished });
        errorStatus = api.Error;
        await refreshTodos();
    }
}
